/*
 * © 2024 huayunliufeng保留所有权利, 依据MIT许可证发布。
 * 请勿更改或删除版权声明或此文件头。
 * 此代码是免费软件, 您可以重新分发和/或修改它。
 * 开源是希望它有用, 但不对代码做任何保证。
 * 如有疑问请联系: huayunliufeng@163.com
 */

package io.github.huayunliufeng.dbinfo.dbinfo;

import io.github.huayunliufeng.common.utils.HylfDataUtil;
import io.github.huayunliufeng.common.utils.HylfFunUtil;
import io.github.huayunliufeng.dbinfo.model.TableExportedKeyInfo;
import lombok.extern.slf4j.Slf4j;

import java.sql.Connection;
import java.sql.ResultSet;
import java.util.List;
import java.util.Map;

/**
 * 获取表的外键信息。
 *
 * @author zhongq
 * @datetime 2024/3/27 17:05
 */
@Slf4j
public class ObtainExportedKeyInfo extends ObtainDbInfo {

    private static Map<Connection, ObtainExportedKeyInfo> obtainExportedKeyInfoMap = null;

    private Map<String, List<TableExportedKeyInfo>> exportedKeyInfoMap = null;

    protected ObtainExportedKeyInfo(Connection conn) {
        super(conn);
    }

    public static ObtainExportedKeyInfo build(Connection conn) {
        obtainExportedKeyInfoMap = HylfFunUtil.mapAddValue(obtainExportedKeyInfoMap, conn, () -> new ObtainExportedKeyInfo(conn));
        return obtainExportedKeyInfoMap.get(conn);
    }

    /**
     * <p>检索引用给定表的主键列(表导出的外键)的外键列的描述。</p>
     * <p>它们按FKTABLE_CAT、FKTABLE_SCHEMA、FKTABLE_NAME和KEY_SEQ排序。</p>
     *
     * @param catalog 目录名称;必须与存储在此数据库中的目录名称匹配;“”检索那些没有目录的;null表示不应使用目录名称来缩小搜索范围
     * @param schema  模式名;必须与存储在数据库中的模式名称匹配;""检索那些没有模式的;null表示不应该使用模式名称来缩小搜索范围
     * @param table   表名;必须匹配表名，因为它存储在这个数据库中
     * @return 每一行都是一个外键列描述
     */
    public List<TableExportedKeyInfo> getExportedKeys(String catalog, String schema, String table) {
        String logFormat = "获取数据库表外键信息失败。[catalog = {}, schema = {}, table = {}]";
        return HylfFunUtil.methodVoidReturnExec(() -> {
            String key = String.join("-", "ExportedKeys", catalog, schema, table);
            exportedKeyInfoMap = HylfFunUtil.mapAddValue(exportedKeyInfoMap, key, () -> {
                ResultSet resultSet = HylfFunUtil.methodVoidReturnExec(
                        () -> databaseMetaData.getExportedKeys(catalog, schema, table)
                        , logFormat, catalog, schema, table);
                List<TableExportedKeyInfo> exportedKeyInfoList = HylfDataUtil.autoSetValue(resultSet, TableExportedKeyInfo.class);
                HylfFunUtil.autoClose(resultSet);
                return exportedKeyInfoList;
            });
            return exportedKeyInfoMap.get(key);
        });
    }

    /**
     * <p>检索引用给定表的主键列(表导出的外键)的外键列的描述。</p>
     * <p>它们按FKTABLE_CAT、FKTABLE_SCHEMA、FKTABLE_NAME和KEY_SEQ排序。</p>
     *
     * @param table 表名;必须匹配表名，因为它存储在这个数据库中
     * @return 每一行都是一个外键列描述
     */
    public List<TableExportedKeyInfo> getExportedKeys(String table) {
        return getExportedKeys(getCatalog(), getSchema(), table);
    }

    /**
     * <p>检索给定外键表中引用主键的外键列的描述，或表示父表的唯一约束的列的描述（可以是相同的表，也可以是不同的表）。</p>
     * <p>从父表返回的列数必须与构成外键的列数相匹配。它们按FKTABLE_CAT、FKTABLE_SCHEM、FKTABLE_NAME和KEY_SEQ排序。</p>
     *
     * @param parentCatalog  目录名;必须与存储在数据库中的目录名称匹配;“”检索那些没有目录的;null表示从选择标准中删除目录名称
     * @param parentSchema   模式名;必须与存储在数据库中的模式名称匹配;""检索那些没有模式的;null表示从选择标准中删除模式名称
     * @param parentTable    导出键的表名;必须匹配存储在数据库中的表名吗
     * @param foreignCatalog 目录名;必须与存储在数据库中的目录名称匹配;“”检索那些没有目录的;null表示从选择标准中删除目录名称
     * @param foreignSchema  模式名;必须与存储在数据库中的模式名称匹配;""检索那些没有模式的;null表示从选择标准中删除模式名称
     * @param foreignTable   导入键的表名;必须匹配存储在数据库中的表名吗
     * @return 每一个外键列描述
     */
    public List<TableExportedKeyInfo> getCrossReference(String parentCatalog, String parentSchema, String parentTable,
                                                        String foreignCatalog, String foreignSchema, String foreignTable) {
        String logFormat = "获取数据库表外键信息失败。[parentCatalog = {}, parentSchema = {}, parentTable = {}, foreignCatalog = {}, foreignSchema = {}, foreignTable = {}]";
        return HylfFunUtil.methodVoidReturnExec(() -> {
            String key = String.join("-", "CrossReference", parentCatalog, parentSchema, parentTable, foreignCatalog, foreignSchema, foreignTable);
            exportedKeyInfoMap = HylfFunUtil.mapAddValue(exportedKeyInfoMap, key, () -> {
                ResultSet resultSet = HylfFunUtil.methodVoidReturnExec(
                        () -> databaseMetaData.getCrossReference(parentCatalog, parentSchema, parentTable, foreignCatalog, foreignSchema, foreignTable)
                        , logFormat, parentCatalog, parentSchema, parentTable, foreignCatalog, foreignSchema, foreignTable);
                List<TableExportedKeyInfo> exportedKeyInfoList = HylfDataUtil.autoSetValue(resultSet, TableExportedKeyInfo.class);
                HylfFunUtil.autoClose(resultSet);
                return exportedKeyInfoList;
            });
            return exportedKeyInfoMap.get(key);
        });
    }

    /**
     * 检索由给定表的外键列(表导入的主键)引用的主键列的描述。它们按PKTABLE_CAT、pktable_schema、PKTABLE_NAME和KEY_SEQ排序。
     *
     * @param catalog 目录名称;必须与存储在数据库中的目录名称匹配;“”检索那些没有目录的;null表示不应使用目录名称来缩小搜索范围
     * @param schema  模式名;必须与存储在数据库中的模式名称匹配;""检索那些没有模式的;null表示不应该使用模式名称来缩小搜索范围
     * @param table   表名;必须匹配存储在数据库中的表名
     * @return 每一行都是一个主键列描述
     */
    public List<TableExportedKeyInfo> getImportedKeys(String catalog, String schema, String table) {
        String logFormat = "获取数据库表ImportedKey失败。[catalog = {}, schema = {}, table = {}]";
        return HylfFunUtil.methodVoidReturnExec(() -> {
            String key = String.join("-", "ImportedKeys", catalog, schema, table);
            exportedKeyInfoMap = HylfFunUtil.mapAddValue(exportedKeyInfoMap, key, () -> {
                ResultSet resultSet = HylfFunUtil.methodVoidReturnExec(
                        () -> databaseMetaData.getImportedKeys(catalog, schema, table)
                        , logFormat, catalog, schema, table);
                List<TableExportedKeyInfo> exportedKeyInfoList = HylfDataUtil.autoSetValue(resultSet, TableExportedKeyInfo.class);
                HylfFunUtil.autoClose(resultSet);
                return exportedKeyInfoList;
            });
            return exportedKeyInfoMap.get(key);
        });
    }

    /**
     * 检索由给定表的外键列(表导入的主键)引用的主键列的描述。它们按PKTABLE_CAT、pktable_schema、PKTABLE_NAME和KEY_SEQ排序。
     *
     * @param table 表名;必须匹配存储在数据库中的表名
     * @return 每一行都是一个主键列描述
     */
    public List<TableExportedKeyInfo> getImportedKeys(String table) {
        return getImportedKeys(getCatalog(), getSchema(), table);
    }
}
